<?php
/**
 * FastApp.
 * 1/3/25
 * @Author deepsea
 * @Contact (telegram:deepsea159)
 */

namespace App\Fastapp\Process\CryptoTransfer;

use App\Fastapp\Api\Service\Crypto\Coin\Ton;
use App\Fastapp\Api\Service\Crypto\Config;
use App\Fastapp\Job\WalletAddressTransactionJob;
use GuzzleHttp\Client;
use Hyperf\Process\AbstractProcess;
use Hyperf\Process\Annotation\Process;
use Olifanton\Interop\Boc\Cell;
use Olifanton\Interop\Bytes;

#[Process(name: 'TonListenerProcess')]
class TonListenerProcess extends AbstractProcess
{
    public Client $client;
    public int $lastSeqno = 0;

    public function isEnable($server): bool
    {
        return false;
//        return env('APP_ENV') == 'prod';
    }

    public function handle(): void
    {
        //Ton Jetton
        $this->client = new Client();
        $response = $this->client->get(Config::TON_URL . '/api/v3/masterchainInfo');
        $this->lastSeqno = json_decode($response->getBody()->getContents(), true)['last']['seqno'];
        $this->start();
    }

    public function formatData(): void
    {
        $blockShards = [];
        try {
            $response = $this->client->get(Config::TON_URL . '/api/v3/masterchainBlockShards', [
                'query' => ['seqno' => $this->lastSeqno]
            ]);
            $blockShards = json_decode($response->getBody()->getContents(), true)['blocks'];
            $this->lastSeqno++;
        } catch (\Throwable) {
        }

        foreach ($blockShards as $blockShard) {
            if ($blockShard['workchain'] == 0) {
                \Hyperf\Coroutine\go(function () use ($blockShard) {
                    $query = ['seqno' => $blockShard['seqno'], 'shard' => $blockShard['shard'], 'workchain' => 0, 'sort' => 'desc'];
                    $response = $this->client->get(Config::TON_URL . '/api/v3/transactions', [
                        'headers' => [
                            'X-Api-Key' => Config::TON_API_KEY
                        ],
                        'query' => $query,
                    ]);
//本币交易：
//收入：in_msg 包含数据，out_msgs 为空。
//支出：in_msg 为空，out_msgs 包含数据。
//USDT交易：
//收入：in_msg 包含数据，out_msgs 为空，且 in_msg 的发送方为USDT合约地址。
//支出：in_msg 为空，out_msgs 包含数据，且 out_msgs 的接收方为USDT合约地址。
                    $data = json_decode($response->getBody()->getContents(), true)['transactions'];
                    $tranTon = $tranUsdt = [];
                    foreach ($data as $transaction) {
                        if ($transaction['description']['aborted'] === true && $transaction['account_state_after']['account_status'] != 'uninit') {
                            continue;
                        }
                        $in = $transaction['in_msg'];
                        $out = $transaction['out_msgs'];
                        if (!empty($in) && empty($out) && !empty($in['source'])) {
                            //usdt
                            if ($in['source'] == Config::TON_CONTRACT_ADDRESS_USDT_HEX) {
                                $data = $this->parseUsdtBody($in['message_content']['body']);
                                $tranUsdt[] = [
                                    'txid' => $transaction['hash'],
                                    'from' => Ton::addressFormat($in['source']),
                                    'to' => $data['address'],
                                    'value' => $data['amount'],
                                ];
                            } else {
                                //本币
                                $tranTon[] = [
                                    'txid' => $transaction['hash'],
                                    'from' => Ton::addressFormat($in['source']),
                                    'to' => Ton::addressFormat($in['destination']),
                                    'value' => $in['value'] / 1e9
                                ];
                            }
                        }
                        if (empty($in) && !empty($out)) {
                            foreach ($out as $item) {
                                if (empty($item['source'])) continue;
                                if ($item['destination'] == Config::TON_CONTRACT_ADDRESS_USDT_HEX) {
                                    $data = $this->parseUsdtBody($item['message_content']['body']);
                                    $tranUsdt[] = [
                                        'txid' => $transaction['hash'],
                                        'from' => Ton::addressFormat($item['source']),
                                        'to' => $data['address'],
                                        'value' => $data['amount'],
                                    ];
                                } else {
                                    $tranTon[] = [
                                        'txid' => $transaction['hash'],
                                        'from' => Ton::addressFormat($out['source']),
                                        'to' => Ton::addressFormat($out['destination']),
                                        'value' => $out['value'] / 1e9
                                    ];
                                }
                            }
                        }
                    }
                    if ($tranTon) {
                        $this->handleEvent($tranTon, 'to');//充值
                        $this->handleEvent($tranTon, 'from');//提现
                    }
                    if ($tranUsdt) {
                        console()->info('--------------usdt------------' . count($tranUsdt));
                        $this->handleEvent($tranUsdt, 'to', 'USDT');//充值
                        $this->handleEvent($tranUsdt, 'from', 'USDT');//提现
                    }
                });
            }
        }
        sleep(1);
    }

    private function handleEvent(array $receivedTransactions, string $field, ?string $symbol_token = null): void
    {
        WalletAddressTransactionJob::log($receivedTransactions, 'TON', $field, $symbol_token);
    }

    public function start(): void
    {
        $this->formatData();
        $this->start();
    }

    private function parseUsdtBody(string $body): array
    {
        $op = bin2hex(substr(base64_decode($body), 0, 4));
        if ($op !== 'b5ee9c72') {
            return [];
        }
        $rootCell = Cell::oneFromBoc($body, true);
        $slice = $rootCell->beginParse();
        $opCode = $slice->loadUint(32)->toBase(16);

        if ($opCode !== 'f8a7ea5') {
            return [];
        }

        $queryId = $slice->loadUint(64)->toBase(16);
        $workchainId = $slice->loadUint(8)->toBase(10);
        $addressHash = Bytes::bytesToHexString($slice->loadBits(256));
        $address = ($workchainId === '255' ? "-1" : $workchainId) . ":" . $addressHash;  // 255 可能表示 -1

        try {
            $amount = $slice->loadCoins();
        } catch (\Exception $e) {
            $amount = $slice->loadUint(128)->toBase(16);
        }

        $amountDecimal = gmp_strval(gmp_init($amount->toBase(10), 16));
        $amountUSDT = bcdiv($amountDecimal, '1000000000', 9);

        return [
            'opCode' => $opCode,
            'queryId' => $queryId,
            'address' => $address,
            'amount' => $amountUSDT,
        ];
    }
}